#pragma once
#include "Common.hpp"
#include "PageCache.hpp"
class CentralCache
{
public:
    static CentralCache* GetInstance()
    {
        return &_sInst;
    }
    Span *GetOneSpan(SpanList &list, size_t size)
    {
        Span *it = list.Begin();
        while (it != list.End())
        {
            if (it->_freeList != nullptr)
            {
                return it;
            }
            else
            {
                it = it->_next;
            }
        }
        list._mutex.unlock();

        PageCache::GetInstance()->_pageMtx.lock();
        // 到PageCache中要空间
        Span *span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
        span->_isUse=true;
        PageCache::GetInstance()->_pageMtx.unlock();

        // 計算span的大快内存的起始地址和大快内存的大小(字节数)
        char *start = (char *)(span->_pageId << PAGE_SHIFT);
        size_t bytes = span->_n << PAGE_SHIFT;
        char *end = start + bytes;
        // 把大块内存切成自由链表链接起来
        // 先切一块下来去做头，方便尾插
        span->_freeList = start;
        start += size;  
        void *tail = span->_freeList;
        while (start < end) 
        {
            NextObj(tail) = start;      //尾插内存块到span的_freeList自由链表中
            tail = NextObj(tail);
            start += size;
        }

        list._mutex.lock();
        list.PushFront(span);
        list._mutex.unlock();

        return span;
    }
    // 从中心缓存获取一定数量的对象给thread cache
    size_t CentralCache::FetchRangeObj(void *&start, void *&end, size_t batchNum, size_t size)
    {
        //	每个桶加锁
        size_t index = SizeClass::Index(size);
        _spanLists[index]._mutex.lock();

        Span *span = GetOneSpan(_spanLists[index], size);
        assert(span);
        assert(span->_freeList);

        // 从span中获取batchNum个对象
        // 如果不够batchNum个，有多少拿多少
        start = span->_freeList;
        end = start;
        size_t i = 0, actualNum = 1;
        while (i < batchNum - 1 && NextObj(end) != nullptr) //	取连续内存块，当batchNum超过原有时，按拥有的数量给
        {
            end = NextObj(end);
            ++i;
            ++actualNum;
        }
        span->_freeList = NextObj(end); // 剩余空间仍挂在Span中的_freeList去
        NextObj(end) = nullptr;         //	最后end记录下一个空间地址置空
        span->_useCount += actualNum;
        _spanLists[index]._mutex.unlock(); //	解锁

        return actualNum;
    }
    void ReleaseListToSpans(void*start,size_t size)
    {
        size_t index=SizeClass::Index(size);
        _spanLists[index]._mutex.lock();
        while(start)
        {
            void* next=NextObj(start);
            Span* span=PageCache::GetInstance()->MapObjectToSpan(start);
            NextObj(start)=span->_freeList; 
            span->_freeList=start;  
            span->_useCount--;  //还内存到最开始的span页中
            if(span->_useCount==0)
            {
                span->_next=nullptr;
                span->_prev=nullptr;
                span->_freeList=nullptr;
                _spanLists[index].Erase(span);
                _spanLists[index]._mutex.unlock();
                PageCache::GetInstance()->_pageMtx.lock();
                PageCache::GetInstance()->ReleaseSpanToPageCache(span);
                PageCache::GetInstance()->_pageMtx.unlock();
            }
            start=next;
        }
        _spanLists[index]._mutex.unlock();
    }
private:
    SpanList _spanLists[NFREELIST];
    static CentralCache  _sInst;
};